//
// exploitable.cpp
//
// The MSEC !exploitable Crash Analyzer
//
//
// Developed by the Microsoft Security Engineering Center (MSEC)
// Copyright 2008-2013, Microsoft Corporation
//
//	Microsoft Public License (Ms-PL)
//	This license governs use of the accompanying software. If you use the software, you accept this license. If you do not accept the license, do not use the software.
//
//	Definitions
//		The terms "reproduce," "reproduction," "derivative works," and "distribution" have the same meaning here as under U.S. copyright law. A "contribution" is the original software, or any additions or changes to the software. A "contributor" is any person that distributes its contribution under this license. "Licensed patents" are a contributor's patent claims that read directly on its contribution.
//	Grant of Rights
//		(A) Copyright Grant- Subject to the terms of this license, including the license conditions and limitations in section 3, each contributor grants you a non-exclusive, worldwide, royalty-free copyright license to reproduce its contribution, prepare derivative works of its contribution, and distribute its contribution or any derivative works that you create.
//		(B) Patent Grant- Subject to the terms of this license, including the license conditions and limitations in section 3, each contributor grants you a non-exclusive, worldwide, royalty-free license under its licensed patents to make, have made, use, sell, offer for sale, import, and/or otherwise dispose of its contribution in the software or derivative works of the contribution in the software.
//	Conditions and Limitations
//		(A) No Trademark License- This license does not grant you rights to use any contributors' name, logo, or trademarks. 
//		(B) If you bring a patent claim against any contributor over patents that you claim are infringed by the software, your patent license from such contributor to the software ends automatically. 
//		(C) If you distribute any portion of the software, you must retain all copyright, patent, trademark, and attribution notices that are present in the software. 
//		(D) If you distribute any portion of the software in source code form, you may do so only under this license by including a complete copy of this license with your distribution. If you distribute any portion of the software in compiled or object code form, you may only do so under a license that complies with this license. 
//		(E) The software is licensed "as-is." You bear the risk of using it. The contributors give no express warranties, guarantees, or conditions. You may have additional consumer rights under your local laws which this license cannot change. To the extent permitted under your local laws, the contributors exclude the implied warranties of merchantability, fitness for a particular purpose and non-infringement.
//


#define EXPLOITABLE_MODULE
#include "stdafx.h"
#include "exploitable.h"
#include "exploitable_rules.h"
#include "metadisassembler.h"
#include "utility.h"
#include "taint.h"
#include <stdarg.h>
#include <stdio.h>


/// Extension Variables
static LOGGING_MODE s_eLoggingMode = HUMAN_NORMAL;
static HASHING_MODE s_eHashingMode = SHA256;
static bool s_fUseJitRecord = false;
static UINT64 s_offJitRecord = 0UL;
static bool s_fUseExplicitMetadisOffset = false;
static UINT64 s_offMetaDis = 0UL;


/// MSEC Extension: !metadiss
///
/// Meta-disassemble the basic block and report the results to the user in human readable form
extern "C" HRESULT CALLBACK
metadis(PDEBUG_CLIENT5 pClient, PCSTR args )
{
	DEBUGGER_CONTROLS objDebugger( pClient );
	DEBUGGER_STATE objState;

	if ( !objDebugger.fInitialized )
		return E_INVALIDARG;

	// Initialize the extension variables
	s_eLoggingMode = HUMAN_VERBOSE;
	s_fUseExplicitMetadisOffset = false;
	s_offMetaDis = 0UL;

	// Parse the command arguments
	if( !ParseMetaDisArguments( args ) )
	{
		Log( objDebugger.pDebugControl, TERSE, HUMAN, L"Error: Invalid command arguments" );
		Log( objDebugger.pDebugControl, MACHINE, L"ERROR:INVALID_ARGUMENTS" );
		return( E_FAIL );
	}

	// Log the version number
	Log( objDebugger.pDebugControl, MACHINE, L"VERSION:%s\n", MSEC_VERSION_W );
	Log( objDebugger.pDebugControl, TERSE, HUMAN, L"\n!metadis %s\n", MSEC_VERSION_W );

	// Gather all the available information
	GatherInitialState( objDebugger, &objState );
	GatherStackInformation( objDebugger, &objState );
	GatherExceptionInformation( objDebugger, &objState );

	if( s_fUseExplicitMetadisOffset )
	{
		objState.offInstruction = s_offMetaDis;
	}	
	GatherFaultingInstruction( objDebugger, &objState );
	GatherBasicBlock( objDebugger, &objState );
	GatherTaintInformation( objDebugger, &objState );

	// And report the basic block
	ReportBasicBlockDetails( objDebugger, objState );

	return( S_OK );
}

/// MSEC Extension: !exploitable
///
/// Evaluate the crash dump for known exploitability issues, and report the results to the user in either
/// human readable or designed-to-be-machine-parsed format
extern "C" HRESULT CALLBACK
exploitable(PDEBUG_CLIENT5 pClient, PCSTR args)
{
	DEBUGGER_CONTROLS objDebugger( pClient );
	DEBUGGER_STATE objState;	
	if ( !objDebugger.fInitialized )
		return E_INVALIDARG;

	// Initialize the extension variables
	s_eLoggingMode = HUMAN_NORMAL;
	s_fUseJitRecord = false;
	s_offJitRecord = 0UL;

	// Parse the command arguments
	if( !ParseExploitableArguments( args ) )
	{
		Log( objDebugger.pDebugControl, TERSE, HUMAN, L"Error: Invalid command arguments" );
		Log( objDebugger.pDebugControl, MACHINE, L"ERROR:INVALID_ARGUMENTS" );
		return( E_FAIL );
	}

	// Log the version number
	Log( objDebugger.pDebugControl, MACHINE, L"VERSION:%s\n", MSEC_VERSION_W );
	Log( objDebugger.pDebugControl, TERSE, HUMAN, L"\n!exploitable %s\n", MSEC_VERSION_W );

	// And now, we process the rules table
	CLASSIFICATION eClassification = UNKNOWN;
	PCWSTR pwzExplanation = NULL;
	PCWSTR pwzDescription = NULL;
	PCWSTR pwzShortDescription = NULL;
	PCWSTR pwzUrl = NULL;
	WCHAR pwzDescriptionBuffer[GENERATED_DESCRIPTION_LENGTH];
	WCHAR pwzShortDescriptionBuffer[GENERATED_DESCRIPTION_LENGTH];

	bool fFinalResultsKnown = false;

	DWORD cRules = sizeof( RULES ) / sizeof( EXPLOITABLE_RULE );
	for( DWORD iRule = 0; !fFinalResultsKnown && (iRule < cRules); iRule++ )
	{
		switch( RULES[iRule].eProcessingType )
		{
			case GATHER_DATA:
			{				
				bool fResult = false;
				
				if( RULES[iRule].pfuncGatherFunction != NULL )
				{					
					fResult = RULES[iRule].pfuncGatherFunction( objDebugger, &objState );				
				}
				else
				{
					Log( objDebugger.pDebugControl, MACHINE, L"ERROR:INVALID_GATHER_RULE_%u\n", iRule );
					Log( objDebugger.pDebugControl, TERSE, HUMAN, L"Error: Rule #%u in !exploitable was an invalid Gather rule\n", iRule );
				}

				if( !fResult )
				{
					Log( objDebugger.pDebugControl, MACHINE, L"ERROR:FAILED_GATHER_RULE_%u\n", iRule );
					Log( objDebugger.pDebugControl, TERSE, HUMAN, L"Error: Gather Rule #%u in !exploitable failed\n", iRule );
					RestoreDebuggerState( objDebugger, objState );
					return( E_FAIL );
				}
			}
			break;

			case REPORT_DATA:
			{				
				if( RULES[iRule].pfuncReportProcedure != NULL )
				{
					RULES[iRule].pfuncReportProcedure( objDebugger, objState );
				}
				else
				{
					Log( objDebugger.pDebugControl, MACHINE, L"ERROR:INVALID_REPORT_RULE_%u\n", iRule );
					Log( objDebugger.pDebugControl, TERSE, HUMAN, L"Error: Rule #%u in !exploitable was an invalid Report rule\n", iRule );
					RestoreDebuggerState( objDebugger, objState );
					return( E_FAIL );
				}
			}
			break;

			case ANALYZE_DATA:
			{
				// Check the rules in order, looking for a match failure
				if( (RULES[iRule].dwProcessorModeRule != DONT_CARE) &&
					( ((RULES[iRule].dwProcessorModeRule == KERNEL) && (objState.dwDebugClass != DEBUG_CLASS_KERNEL) ) ||
					  ((RULES[iRule].dwProcessorModeRule == USER) && (objState.dwDebugClass != DEBUG_CLASS_USER_WINDOWS)) ) )
				{
					continue;
				}

				if( (RULES[iRule].dwExceptionAddressRangeRule != DONT_CARE) && 
					( ((RULES[iRule].dwExceptionAddressRangeRule == IN_KERNEL_MEMORY) && objState.fExceptionAddressInUserMode) ||
					  ((RULES[iRule].dwExceptionAddressRangeRule == IN_USER_MEMORY) && !objState.fExceptionAddressInUserMode) ||
					  ((RULES[iRule].dwExceptionAddressRangeRule == NEAR_NULL) && !objState.fExceptionAddressNearNull) ||
					  ((RULES[iRule].dwExceptionAddressRangeRule == NOT_NEAR_NULL) && objState.fExceptionAddressNearNull) ) )
				{
					continue;
				}

				if( (RULES[iRule].dwExceptionTypeRule != DONT_CARE) && 
					(RULES[iRule].dwExceptionTypeRule != objState.objException.ExceptionRecord.ExceptionCode ) )
				{
					continue;
				}

				if( (RULES[iRule].qwExceptionSubTypeRule != DONT_CARE) &&
					(RULES[iRule].qwExceptionSubTypeRule != objState.qwExceptionSubtype) )
				{
					continue;
				}

				if( (RULES[iRule].dwExceptionLevelRule != DONT_CARE) &&
					( ((RULES[iRule].dwExceptionLevelRule == FIRST_CHANCE) && !objState.objException.FirstChance) ||
					  ((RULES[iRule].dwExceptionLevelRule == SECOND_CHANCE) && objState.objException.FirstChance) ) )
				{
					continue;
				}

				if( RULES[iRule].pfuncAnalysisFunction != NULL )
				{
					if( !RULES[iRule].pfuncAnalysisFunction( objState ) )
					{
						continue;
					}
				}

				// The rules have all been fired, if we've gotten this far, we have a match
				eClassification = RULES[iRule].eResult;
				pwzExplanation = RULES[iRule].pwzExplanation;
				pwzDescription = RULES[iRule].pwzDescription;
				pwzShortDescription = RULES[iRule].pwzShortDescription;
				pwzUrl = RULES[iRule].pwzUrl;

				fFinalResultsKnown = RULES[iRule].fIsFinal;
			}
			break;
		}
	}

	// Determine the exception name, if one wasn't determined by the rule
	if( eClassification != NOT_AN_EXCEPTION )
	{
		if( (pwzDescription == NULL) || (pwzShortDescription == NULL) )
		{
			if( (objState.objException.ExceptionRecord.ExceptionCode & 0xffffffff) == STATUS_ACCESS_VIOLATION )
			{
				switch( objState.objException.ExceptionRecord.ExceptionInformation[0] )
				{
				case ACCESS_VIOLATION_TYPE_WRITE:
					pwzDescription = pwzDescription ? pwzDescription : L"Write Access Violation";
					pwzShortDescription = pwzShortDescription ? pwzShortDescription : L"WriteAV";
					break;

				case ACCESS_VIOLATION_TYPE_READ:
					pwzDescription = pwzDescription ? pwzDescription : L"Read Access Violation";
					pwzShortDescription = pwzShortDescription ? pwzShortDescription : L"ReadAV";
					break;

				case ACCESS_VIOLATION_TYPE_DEP:
					pwzDescription = pwzDescription ? pwzDescription : L"Data Execution Protection Violation";
					pwzShortDescription = pwzShortDescription ? pwzShortDescription : L"DEPViolation";
					break;
				}
			}
			else
			{
				if( pwzDescription == NULL ) 
				{
					for (int iException = 0; EXCEPTION_NAME_DECODE_MAP[iException].name != NULL; iException++)
					{
						if (EXCEPTION_NAME_DECODE_MAP[iException].code == (objState.objException.ExceptionRecord.ExceptionCode & 0xffffffff))
						{
							pwzDescription = EXCEPTION_NAME_DECODE_MAP[iException].name;
							break;
						}
					}
				}

				if( pwzShortDescription == NULL ) 
				{
					for (int iException = 0; SHORT_EXCEPTION_NAME_DECODE_MAP[iException].name != NULL; iException++)
					{
						if (SHORT_EXCEPTION_NAME_DECODE_MAP[iException].code == (objState.objException.ExceptionRecord.ExceptionCode & 0xffffffff))
						{
							pwzShortDescription = SHORT_EXCEPTION_NAME_DECODE_MAP[iException].name;
							break;
						}
					}
				}
			}

			if( pwzDescription == NULL )
			{
				_snwprintf_s( pwzDescriptionBuffer, GENERATED_DESCRIPTION_LENGTH, _TRUNCATE, L"Error Code (0x%x)", (objState.objException.ExceptionRecord.ExceptionCode & 0xffffffff) );
				pwzDescription = pwzDescriptionBuffer;
			}

			if( pwzShortDescription == NULL )
			{
				_snwprintf_s( pwzShortDescriptionBuffer, GENERATED_DESCRIPTION_LENGTH, _TRUNCATE, L"0x%x", (objState.objException.ExceptionRecord.ExceptionCode & 0xffffffff) );
				pwzShortDescription = pwzShortDescriptionBuffer;
			}
		}
	}
	else
	{
		pwzDescription = L"Not an Exception";
		pwzShortDescription = L"NotAnException";
	}

	// Log the stack information
	//
	// This is done here, because it can be set by multiple gather rules
	LogStackInformation( objDebugger, objState );

	// Log the information on the faulting instruction
	// 
	// This is done here, because it can be set by multiple gather rules
	Log( objDebugger.pDebugControl, MACHINE, L"INSTRUCTION_ADDRESS:0x%16.16I64x\n", objState.offInstruction );
	Log( objDebugger.pDebugControl, VERBOSE, HUMAN, L"Instruction Address: 0x%16.16I64x\n", objState.offInstruction );
	Log( objDebugger.pDebugControl, MACHINE, L"INVOKING_STACK_FRAME:%d\n", objState.iInvokingStackFrame );
	
	if( objState.fSourceInformationAvailable )
	{
		Log( objDebugger.pDebugControl, MACHINE, L"SOURCE_FILE:%s\n", objState.pwzFaultingFile );
		Log( objDebugger.pDebugControl, VERBOSE, HUMAN, L"Source File: %s\n", objState.pwzFaultingFile );
		Log( objDebugger.pDebugControl, MACHINE, L"SOURCE_LINE:%u\n", objState.dwFaultingLine );
		Log( objDebugger.pDebugControl, VERBOSE, HUMAN, L"Source Line: %u\n", objState.dwFaultingLine );	
	}
	
	// Log the description
	Log( objDebugger.pDebugControl, MACHINE, L"DESCRIPTION:%s\n", pwzDescription );
	Log( objDebugger.pDebugControl, VERBOSE, HUMAN, L"\nDescription: %s\n", pwzDescription );

	Log( objDebugger.pDebugControl, MACHINE, L"SHORT_DESCRIPTION:%s\n", pwzShortDescription );
	Log( objDebugger.pDebugControl, VERBOSE, HUMAN, L"Short Description: %s\n", pwzShortDescription );

	// Log the classification
	Log( objDebugger.pDebugControl, MACHINE, L"CLASSIFICATION:%s\n", CLASSIFICATION_TABLE[eClassification] );
	Log( objDebugger.pDebugControl, TERSE, HUMAN, L"Exploitability Classification: %s\n", CLASSIFICATION_TABLE[eClassification] );

	// Log the bug title
	switch( eClassification )
	{
		case EXPLOITABLE:
			Log( objDebugger.pDebugControl, TERSE, MACHINE, L"BUG_TITLE:Exploitable - %s starting at %s (Hash=0x%8.8x.0x%8.8x)\n",
														    pwzDescription,
															objState.pwzFaultingLocation,
														    objState.dwMajorHash,
															objState.dwMinorHash );
			Log( objDebugger.pDebugControl, TERSE, HUMAN, L"Recommended Bug Title: Exploitable - %s starting at %s (Hash=0x%8.8x.0x%8.8x)\n",
														   pwzDescription,
														   objState.pwzFaultingLocation,
														   objState.dwMajorHash,
															objState.dwMinorHash );
			break;

		case PROBABLY_EXPLOITABLE:
			Log( objDebugger.pDebugControl, TERSE, MACHINE, L"BUG_TITLE:Probably Exploitable - %s starting at %s (Hash=0x%8.8x.0x%8.8x)\n",
														    pwzDescription,
															objState.pwzFaultingLocation,
														    objState.dwMajorHash,
															objState.dwMinorHash );
			Log( objDebugger.pDebugControl, TERSE, HUMAN, L"Recommended Bug Title: Probably Exploitable - %s starting at %s (Hash=0x%8.8x.0x%8.8x)\n",
														   pwzDescription,
														   objState.pwzFaultingLocation,
														   objState.dwMajorHash,
															objState.dwMinorHash );
			break;

		case NOT_AN_EXCEPTION:
			break;

		default:
			Log( objDebugger.pDebugControl, TERSE, MACHINE, L"BUG_TITLE:%s starting at %s (Hash=0x%8.8x.0x%8.8x)\n",
														    pwzDescription,
															objState.pwzFaultingLocation,
														    objState.dwMajorHash,
															objState.dwMinorHash );
			Log( objDebugger.pDebugControl, TERSE, HUMAN, L"Recommended Bug Title: %s starting at %s (Hash=0x%8.8x.0x%8.8x)\n",
														   pwzDescription,
														   objState.pwzFaultingLocation,
														   objState.dwMajorHash,
															objState.dwMinorHash );
			break;

	}

	// And log the explanation
	if( pwzExplanation != NULL )
	{
		Log( objDebugger.pDebugControl, TERSE, HUMAN, L"\n%s\n", pwzExplanation );
		Log( objDebugger.pDebugControl, MACHINE, L"EXPLANATION:%s", pwzExplanation );
	}

	// And the URL
	if( pwzUrl != NULL )
	{
		Log( objDebugger.pDebugControl, TERSE, HUMAN, L"\n%s\n", pwzUrl );
		Log( objDebugger.pDebugControl, MACHINE, L"URL:%s", pwzUrl );
	}

	// And  we're done
	RestoreDebuggerState( objDebugger, objState );
	return( S_OK );
}




/// 
/// Log the stack and hash information
///
void
LogStackInformation( const DEBUGGER_CONTROLS &objControls, const DEBUGGER_STATE &objState )
{
	ULONG iframeMajor = 0;
	// Log the major/minor hash buckets
	Log( objControls.pDebugControl, MACHINE, L"MAJOR_HASH:0x%8.8x\n", objState.dwMajorHash );
	Log( objControls.pDebugControl, MACHINE, L"MINOR_HASH:0x%8.8x\n", objState.dwMinorHash );

	Log( objControls.pDebugControl, VERBOSE, HUMAN, L"\nException Hash (Major/Minor): 0x%8.8x.0x%8.8x\n", objState.dwMajorHash, objState.dwMinorHash );
	
	// Log the stack
	Log( objControls.pDebugControl, VERBOSE, HUMAN, L"\n Hash Usage : Stack Trace:\n" );
	Log( objControls.pDebugControl, MACHINE, L"STACK_DEPTH:%u\n", objState.cStackFrames );

	for( ULONG iFrame = 0; iFrame < objState.cStackFrames; iFrame++ )
	{
		ULONG64 offDisplacement;
		
		WCHAR pwzSymbol[128];
		WCHAR pwzHashUsage[16];
		 
		HRESULT dwResult = objControls.pDebugSymbols->GetNameByOffsetWide(objState.pStack[iFrame].InstructionOffset, pwzSymbol , 128, NULL, (PULONG64)&offDisplacement);

		if (objState.pfExcludedStackFrames == NULL)
		{
			wcscpy_s (pwzHashUsage, L"Unknown     :");
		}
		else if (objState.pfExcludedStackFrames[iFrame] == TRUE)
		{
			wcscpy_s (pwzHashUsage, L"Excluded    :");
		} else if (iframeMajor < 5) {	
			++iframeMajor;
			wcscpy_s (pwzHashUsage, L"Major+Minor :");
		}
		else {
			wcscpy_s (pwzHashUsage, L"Minor       :");
		}
		
		if( dwResult != E_FAIL )
		{
			Log( objControls.pDebugControl, MACHINE, L"STACK_FRAME:%s+0x%I64x\n", pwzSymbol, offDisplacement );
			Log( objControls.pDebugControl, VERBOSE, HUMAN,     L"%s %s+0x%I64x\n",pwzHashUsage, pwzSymbol, offDisplacement );
		}
		else
		{
			Log( objControls.pDebugControl, MACHINE, L"STACK_FRAME:Unknown\n" );
			Log( objControls.pDebugControl, VERBOSE, HUMAN, L"%s Unknown\n", pwzHashUsage);
		}
	}
}


///
/// Parse the arguments for !exploitable
///
bool
ParseExploitableArguments( PCSTR pszArguments )
{
	PCHAR pszContext = NULL;
	PCHAR pszModifiableArguments = _strdup( pszArguments );

	if( pszModifiableArguments == NULL )
	{
		return( false );
	}

	PCHAR pszToken = strtok_s( pszModifiableArguments, " \t", &pszContext );

	while( pszToken != NULL )
	{
		if( _stricmp( pszToken, "-v" ) == 0 )
		{
			SetLoggingMode( HUMAN_VERBOSE );
		}
		else if( _stricmp( pszToken, "-m" ) == 0 )
		{
			SetLoggingMode( MACHINE_NORMAL );
		}
		else if (_stricmp (pszToken, "-Hash:CustomV1" ) == 0)
		{
			SetHashingMode( CUSTOMV1 );
		}
		else if (_stricmp (pszToken, "-Hash:CustomV2" ) == 0)
		{
			SetHashingMode( CUSTOMV2 );
		}
		else if (_stricmp (pszToken, "-Hash:SHA256" ) == 0)
		{
			SetHashingMode( SHA256 );
		}
		else if (_strnicmp( pszToken, "-jit:", 5 ) == 0 )
		{
			s_fUseJitRecord = true;
			s_offJitRecord = strtoul( &pszToken[5], NULL, 0 );
		}
		else
		{
			free( pszModifiableArguments );
			return( false );
		}

		pszToken = strtok_s( NULL, " \t", &pszContext );
	}

	free( pszModifiableArguments );
	return( true );
}


///
/// Parse the arguments for !metadis
///
bool
ParseMetaDisArguments( PCSTR pszArguments )
{
	PCHAR pszContext = NULL;
	PCHAR pszModifiableArguments = _strdup( pszArguments );

	if( pszModifiableArguments == NULL )
	{
		return( false );
	}

	PCHAR pszToken = strtok_s( pszModifiableArguments, " \t", &pszContext );

	while( pszToken != NULL )
	{
		if (_strnicmp( pszToken, "-addr:", 6 ) == 0 )
		{
			s_fUseExplicitMetadisOffset = true;
			s_offMetaDis = strtoul( &pszToken[6], NULL, 0 );
		}
		else
		{
			free( pszModifiableArguments );
			return( false );
		}

		pszToken = strtok_s( NULL, " \t", &pszContext );
	}

	free( pszModifiableArguments );
	return( true );
}

///
// Take any actions that may need to be done to restore the debugger state
///
void
RestoreDebuggerState( const DEBUGGER_CONTROLS &objControls, const DEBUGGER_STATE &objState )
{
	//
	// Debugger Class Specific "undo" actions
	//
	switch( objState.dwDebugClass )
	{
		case DEBUG_CLASS_KERNEL:
			{
				objControls.pDebugControl->ExecuteWide( DEBUG_OUTCTL_IGNORE, L".trap", DEBUG_EXECUTE_NOT_LOGGED );
			}
			break;

		case DEBUG_CLASS_USER_WINDOWS:
			{
				objControls.pDebugControl->ExecuteWide( DEBUG_OUTCTL_IGNORE, L".cxr", DEBUG_EXECUTE_NOT_LOGGED );
			}
			break;
	}

}


//////////////////////////////////////////////////////////////////////////////////////////////////////
// Gather Functions:
//////////////////////////////////////////////////////////////////////////////////////////////////////

///
/// Gather the initial state of the debugger including the event information, debugger identity, and stack trace
///
bool
GatherInitialState( const DEBUGGER_CONTROLS &objControls, __in DEBUGGER_STATE *pState )
{
	if( pState == NULL )
	{
		return( false );
	}

	// Determine target information from the last event if we haven't got a JIT record to process
	if( !s_fUseJitRecord )
	{
		// Gather the initial information, including the event type
		if( objControls.pDebugControl->GetLastEventInformation( &pState->dwEventType,
			&pState->dwProcessId,
			&pState->dwThreadId,
			NULL,
			NULL,
			NULL,
			NULL,
			0,
			NULL) != S_OK )
		{
			Log( objControls.pDebugControl, MACHINE, L"ERROR:NO_INFORMATION\n" );
			Log( objControls.pDebugControl, TERSE, HUMAN, L"Error: Unable to extract process information from the debugger.\n" );
			return( false );
		}

		// Get the effective target processor
		if( objControls.pDebugControl->GetEffectiveProcessorType( &pState->dwProcessor ) != S_OK )
		{
			Log( objControls.pDebugControl, MACHINE, L"ERROR:NO_PROCESSOR_INFORMATION\n" );
			Log( objControls.pDebugControl, TERSE, HUMAN, L"Error: Unable to determine the effective type of the executing processor.\n" );
			return( false );
		}

		// Get the target processor
		if( objControls.pDebugControl->GetActualProcessorType( &pState->dwActualProcessor ) != S_OK )
		{
			Log( objControls.pDebugControl, MACHINE, L"ERROR:NO_ACTUAL_PROCESSOR_INFORMATION\n" );
			Log( objControls.pDebugControl, TERSE, HUMAN, L"Error: Unable to determine the type of the executing processor.\n" );
			return( false );
		}
	}
	else
	{
		// Load the JIT Record: 
		//
		// Note that as part of this we switch our context to be the correct context for the JIT Record.
		JIT_DEBUG_INFO objJitRecord;
		ULONG dwSizeRead;
		HRESULT dwReadResult;

		dwReadResult = objControls.pDebugDataSpaces->ReadVirtual( s_offJitRecord, &objJitRecord, sizeof( JIT_DEBUG_INFO ), &dwSizeRead );

		if( (dwReadResult == S_OK) && (dwSizeRead == sizeof( JIT_DEBUG_INFO ) ) )
		{
			pState->dwEventType = DEBUG_EVENT_EXCEPTION;
			pState->dwProcessor = objJitRecord.dwProcessorArchitecture;
			
			if( objControls.pSystemObjects->GetThreadIdBySystemId( objJitRecord.dwThreadID, &pState->dwThreadId ) != S_OK )
			{
				Log( objControls.pDebugControl, MACHINE, L"ERROR:UNABLE_TO_DETERMINE_TO_JIT_THREAD\n" );
				Log( objControls.pDebugControl, TERSE, HUMAN, L"Error: Unable to determine the thread from the JIT record.\n" );
				return( false );
			}

			if( objControls.pSystemObjects->SetCurrentThreadId( pState->dwThreadId ) != S_OK )
			{
				Log( objControls.pDebugControl, MACHINE, L"ERROR:UNABLE_TO_SWITCH_TO_JIT_THREAD\n" );
				Log( objControls.pDebugControl, TERSE, HUMAN, L"Error: Unable to switch to the thread for the JIT record.\n" );
				return( false );
			}

			if(  objControls.pSystemObjects->GetCurrentProcessId( &pState->dwProcessId ) != S_OK )
			{
				Log( objControls.pDebugControl, MACHINE, L"ERROR:UNABLE_TO_GET_JIT_PROCESSID\n" );
				Log( objControls.pDebugControl, TERSE, HUMAN, L"Error: Unable to get the process ID for the JIT record.\n" );
				return( false );
			}

			// Get the target processor information: The information in the JIT Record is not necessarily accurate

			// Get the effective target processor
			if( objControls.pDebugControl->GetEffectiveProcessorType( &pState->dwProcessor ) != S_OK )
			{
				Log( objControls.pDebugControl, MACHINE, L"ERROR:NO_PROCESSOR_INFORMATION\n" );
				Log( objControls.pDebugControl, TERSE, HUMAN, L"Error: Unable to determine the effective type of the executing processor.\n" );
				return( false );
			}

			// Get the target processor
			if( objControls.pDebugControl->GetActualProcessorType( &pState->dwActualProcessor ) != S_OK )
			{
				Log( objControls.pDebugControl, MACHINE, L"ERROR:NO_ACTUAL_PROCESSOR_INFORMATION\n" );
				Log( objControls.pDebugControl, TERSE, HUMAN, L"Error: Unable to determine the type of the executing processor.\n" );
				return( false );
			}
		}
		else
		{
			Log( objControls.pDebugControl, MACHINE, L"ERROR:UNABLE_TO_READ_JIT_RECORD\n" );
			Log( objControls.pDebugControl, TERSE, HUMAN, L"Error: Unable to load the JIT record.\n" );
			return( false );
		}
	}

	// Gather the debugging information
	if( objControls.pDebugControl->GetDebuggeeType(&pState->dwDebugClass, &pState->dwDebugQualifier) != S_OK )
	{
		Log( objControls.pDebugControl, MACHINE, L"ERROR:NO_TYPE_MODE\n" );
		Log( objControls.pDebugControl, TERSE, HUMAN, L"Error: Unable to determine the debugging mode or quality from the debugger.\n" );
		return( false );
	}

	// Gather the identity of the debugger
	ULONG cchIdentity;
	objControls.pDebugClient->GetIdentityWide( NULL, 0, &cchIdentity );

	if( cchIdentity > 0 )
	{
		pState->pwzIdentity = (PWCHAR) malloc( cchIdentity * sizeof( WCHAR ) );

		if( pState->pwzIdentity == NULL )
		{
			Log( objControls.pDebugControl, MACHINE, L"ERROR:NO_IDENTITY_OUT_OF_MEMORY\n" );
			Log( objControls.pDebugControl, TERSE, HUMAN, L"Error: Unable to determine the identity of the system because we ran out of memory.\n" );
			return( false );
		}

		if( objControls.pDebugClient->GetIdentityWide( pState->pwzIdentity, cchIdentity, NULL ) != S_OK )
		{
			Log( objControls.pDebugControl, MACHINE, L"ERROR:NO_IDENTITY\n" );
			Log( objControls.pDebugControl, TERSE, HUMAN, L"Error: Unable to determine the identity of the system.\n" );
			return( false );
		}
	}
	else
	{
		Log( objControls.pDebugControl, MACHINE, L"ERROR:NO_IDENTITY\n" );
		Log( objControls.pDebugControl, TERSE, HUMAN, L"Error: Unable to determine the identity of the system.\n" );
		return( false );
	}

	return( true );
}

///
/// Gather the information about the exception
///
bool
GatherExceptionInformation( const DEBUGGER_CONTROLS &objControls, __in DEBUGGER_STATE *pState )
{
	bool fNewExceptionLoaded = false;
	bool fDumpExceptionRecordLoaded = false;

	// Get the exception information for the event, unless we are using a JIT record, in which case it
	// has already been loaded for us
	if( !s_fUseJitRecord )
	{
		if( objControls.pDebugControl->GetLastEventInformation( &pState->dwEventType,
																&pState->dwProcessId,
																&pState->dwThreadId,
																&pState->objException,
																sizeof( pState->objException ),
																NULL,
																NULL,
																0,
																NULL) != S_OK )
		{
			Log( objControls.pDebugControl, MACHINE, L"ERROR:NO_EXCEPTION_INFORMATION\n" );
			Log( objControls.pDebugControl, TERSE, HUMAN, L"Error: Unable to extract the detailed exception information from the debugger.\n" );
			return( false );
		}

		// In the case that we're coming in on a user mode mini-dump, we'll also explicitly set our context record to match 
		// the exception record we just pulled in. Please note that .ecxr is only valid against user mode mini-dumps
		if( (pState->dwDebugClass == DEBUG_CLASS_USER_WINDOWS) && (pState->dwDebugQualifier == DEBUG_USER_WINDOWS_SMALL_DUMP) )
		{
			objControls.pDebugControl->ExecuteWide( DEBUG_OUTCTL_IGNORE, L".ecxr", DEBUG_EXECUTE_NOT_LOGGED );
			fDumpExceptionRecordLoaded = true;
		}
	}
	else
	{
		// Load the JIT Record: 
		//
		// Note that as part of this we switch our context to be the correct context for the JIT Record.
		JIT_DEBUG_INFO objJitRecord;
		ULONG dwSizeRead;
		HRESULT dwReadResult;

		dwReadResult = objControls.pDebugDataSpaces->ReadVirtual( s_offJitRecord, &objJitRecord, sizeof( JIT_DEBUG_INFO ), &dwSizeRead );

		if( (dwReadResult == S_OK) && (dwSizeRead == sizeof( JIT_DEBUG_INFO ) ) )
		{
			fNewExceptionLoaded = LoadExceptionRecordFromPointer( objControls, pState, s_offJitRecord + 24UL );
			(void) LoadContextRecordFromPointer( objControls, s_offJitRecord + 32UL );
		}

		if( !fNewExceptionLoaded ) 
		{
			Log( objControls.pDebugControl, MACHINE, L"ERROR:UNABLE_TO_LOAD_JIT_EXCEPTION_INFORMATION\n" );
			Log( objControls.pDebugControl, TERSE, HUMAN, L"Error: Unable to extract the detailed exception information from the JIT record.\n" );
			return( false );
		}
	}

	// And look to see if this is one of the cases where the actual exception record needs to be extracted
	// or constructed
	fNewExceptionLoaded = fNewExceptionLoaded || FindExceptionInformation( objControls, pState );

	// If we have loaded a new exception, clear our stack, and regather it
	if( fNewExceptionLoaded || fDumpExceptionRecordLoaded )
	{
		ClearStackInformation( pState );
		GatherStackInformation( objControls, pState );
	}

	// Flag the exception information as loaded
	pState->fExceptionLoaded = true;

	// Cache the exception sub type information (if any)
	switch( pState->objException.ExceptionRecord.ExceptionCode & 0xffffffff )
	{
		case STATUS_ACCESS_VIOLATION:
			pState->qwExceptionSubtype = pState->objException.ExceptionRecord.ExceptionInformation[0];
			break;
		default:
			break;
	}

	// Cache the faulting address information
	switch( pState->objException.ExceptionRecord.ExceptionCode & 0xffffffff )
	{
		case STATUS_ACCESS_VIOLATION:
			pState->offFaultingAddress = pState->objException.ExceptionRecord.ExceptionInformation[1];
			break;
		default:
			pState->offFaultingAddress = pState->objException.ExceptionRecord.ExceptionAddress;
			break;
	}

	// And cache the memory range information
	switch( pState->dwProcessor )
	{
		case IMAGE_FILE_MACHINE_I386:
		case IMAGE_FILE_MACHINE_ARM:
		case IMAGE_FILE_MACHINE_THUMB:
		case IMAGE_FILE_MACHINE_ARMNT:
			pState->fExceptionAddressInUserMode = pState->offFaultingAddress < 0x80000000;
			break;

		case IMAGE_FILE_MACHINE_AMD64:
			pState->fExceptionAddressInUserMode = pState->offFaultingAddress < 0x8000000000000000;
			break;

		default:
			pState->fExceptionAddressInUserMode = true;
			break;
	}

	pState->fExceptionAddressNearNull = pState->offFaultingAddress < (64*1024);

	return( true );
}

///
// Find the actual exception address, rather than the breakpoint exception on the top of the stack. If a new
// exception was loaded, the function will return true, otherwise it will return false
///
bool
FindExceptionInformation( const DEBUGGER_CONTROLS &objControls, __in DEBUGGER_STATE *pState )
{
	if( pState->dwDebugClass == DEBUG_CLASS_USER_WINDOWS )
	{
		return( FindUserModeExceptionInformation( objControls, pState ) );
	}
	else if( pState->dwDebugClass == DEBUG_CLASS_KERNEL )
	{
		return( FindKernelModeExceptionInformation( objControls, pState ) );
	}
	else
	{
		return( false );
	}
}

///
// Find the actual exception address, rather than the breakpoint exception on the top of the stack. If a new
// exception was loaded, the function will return true, otherwise it will return false
///
bool
FindUserModeExceptionInformation( const DEBUGGER_CONTROLS &objControls, __in DEBUGGER_STATE *pState )
{
	bool fExceptionLoaded = false;
	ULONG64 offPointerSize;

	// Determine the pointer offset for supported processors. If we don't have a supported processor
	// go ahead and return out
	switch( pState->dwProcessor )
	{
	case IMAGE_FILE_MACHINE_I386:
	case IMAGE_FILE_MACHINE_ARM:
	case IMAGE_FILE_MACHINE_THUMB:
	case IMAGE_FILE_MACHINE_ARMNT:
		offPointerSize = 4;
		break;

	case IMAGE_FILE_MACHINE_AMD64:
		offPointerSize = 8;
		break;

	default:
		return( fExceptionLoaded );
	}

	switch( pState->objException.ExceptionRecord.ExceptionCode & 0xffffffff )
	{

		case STATUS_BREAKPOINT:
		case STATUS_WX86_BREAKPOINT:
			{
				for( ULONG iFrame = 0; !fExceptionLoaded && (iFrame < pState->cStackFrames); iFrame++ )
				{
					WCHAR pwzName[128];
					HRESULT dwResult = objControls.pDebugSymbols->GetNameByOffsetWide(pState->pStack[iFrame].InstructionOffset, pwzName, 128, NULL, NULL );

					if( dwResult != E_FAIL )
					{
						if( (wcscmp( pwzName, L"kernel32!WerpReportFault" ) == 0 ) &&
							(pState->pStack[iFrame].Params[0] != 0) )
						{
							// We have a Windows Error Reporting fault with an exception, it's time to replace our exception with the faulting exception
							fExceptionLoaded = LoadExceptionRecordFromPointer( objControls, pState, pState->pStack[iFrame].Params[0] );

							if( fExceptionLoaded )
							{
								(void) LoadContextRecordFromPointer( objControls, pState->pStack[iFrame].Params[0] + offPointerSize );
							}
						}
						else if( (wcscmp( pwzName, L"kernel32!UnhandledExceptionFilter" ) == 0 )&&
							(pState->pStack[iFrame].Params[0] != 0) )
						{
							// We have an unhandled exception filter, it's time to replace our exception with the faulting exception
							fExceptionLoaded = LoadExceptionRecordFromPointer( objControls, pState, pState->pStack[iFrame].Params[0] );

							if( fExceptionLoaded )
							{
								(void) LoadContextRecordFromPointer( objControls, pState->pStack[iFrame].Params[0] + offPointerSize );
							}
						}

						else if(wcscmp( pwzName, L"vrfcore!VerifierStopMessageEx" ) == 0 )
						{
							// Flag that we have an AppVerifier stop, for rules purposes
							pState->fApplicationVerifierStopDetected = true;
						}
					}
				}
			}			
			break;
	}
			
	// And return whether or not we updated the exception
	return( fExceptionLoaded );
}


///
// Derive the exception from the bug check information, rather than from the breakpoint at the top of the
// stack. If a new exception was loaded, the function will return true, otherwise it will return false
///
bool
FindKernelModeExceptionInformation( const DEBUGGER_CONTROLS &objControls, __in DEBUGGER_STATE *pState )
{
	bool fExceptionLoaded = false;
	ULONG64 offPointerSize;

	// Determine the pointer offset for supported processors. If we don't have a supported processor
	// go ahead and return out
	switch( pState->dwProcessor )
	{
		case IMAGE_FILE_MACHINE_I386:
		case IMAGE_FILE_MACHINE_ARM:
		case IMAGE_FILE_MACHINE_THUMB:
		case IMAGE_FILE_MACHINE_ARMNT:
			offPointerSize = 4;
			break;

		case IMAGE_FILE_MACHINE_AMD64:
			offPointerSize = 8;
			break;

		default:
			return( fExceptionLoaded );
	}

	// Check for a bug check record
	ULONG dwBugCheckCode;
	ULONG64 pqwBugCheckArguments[4];
	HRESULT dwResult = objControls.pDebugControl->ReadBugCheckData( &dwBugCheckCode, &pqwBugCheckArguments[0], &pqwBugCheckArguments[1], &pqwBugCheckArguments[2], &pqwBugCheckArguments[3] );

	if( dwResult == S_OK )
	{
		switch( dwBugCheckCode )
		{
			// KMODE_EXCEPTION_NOT_HANDLED
			case 0x1e:
				{
					pState->objException.ExceptionRecord.ExceptionCode = (DWORD) pqwBugCheckArguments[0];
					pState->objException.ExceptionRecord.ExceptionAddress = pqwBugCheckArguments[1];
					pState->objException.ExceptionRecord.ExceptionFlags = 0;
					pState->objException.ExceptionRecord.ExceptionRecord = 0;
					pState->objException.ExceptionRecord.NumberParameters = 2;
					memset( pState->objException.ExceptionRecord.ExceptionInformation, 0, sizeof( pState->objException.ExceptionRecord.ExceptionInformation ) );
					pState->objException.ExceptionRecord.ExceptionInformation[0] = pqwBugCheckArguments[2];
					pState->objException.ExceptionRecord.ExceptionInformation[1] = pqwBugCheckArguments[3];
					pState->objException.FirstChance = false;
					fExceptionLoaded = true;
				}
				break;

			// PAGE_FAULT_IN_NONPAGED_AREA
			// DRIVER_PAGE_FAULT_IN_FREED_SPECIAL_POOL
			case 0x50:
			case 0xd5:
				{
					pState->objException.ExceptionRecord.ExceptionCode = STATUS_ACCESS_VIOLATION;
					pState->objException.ExceptionRecord.ExceptionAddress = pqwBugCheckArguments[2];
					pState->objException.ExceptionRecord.ExceptionFlags = 0;
					pState->objException.ExceptionRecord.ExceptionRecord = 0;
					pState->objException.ExceptionRecord.NumberParameters = 2;
					memset( pState->objException.ExceptionRecord.ExceptionInformation, 0, sizeof( pState->objException.ExceptionRecord.ExceptionInformation ) );
					pState->objException.ExceptionRecord.ExceptionInformation[0] = pqwBugCheckArguments[1] ? ACCESS_VIOLATION_TYPE_WRITE : ACCESS_VIOLATION_TYPE_READ;
					pState->objException.ExceptionRecord.ExceptionInformation[1] = pqwBugCheckArguments[0];
					pState->objException.FirstChance = false;
					fExceptionLoaded = true;
				}
				break;

			// SYSTEM_THREAD_EXCEPTION_NOT_HANDLED
			case 0x7e:
			case 0x1000007e:
				{
					LoadExceptionRecord( objControls, pState, pqwBugCheckArguments[2] );
					LoadContextRecord( objControls, pqwBugCheckArguments[3] );
					fExceptionLoaded = true;
				}
				break;

			// KERNEL_MODE_EXCEPTION_NOT_HANDLED
			case 0x8e:
			case 0x1000008e:
				{
					pState->objException.ExceptionRecord.ExceptionCode = (DWORD) pqwBugCheckArguments[0];
					pState->objException.ExceptionRecord.ExceptionAddress = pqwBugCheckArguments[1];
					pState->objException.ExceptionRecord.ExceptionFlags = 0;
					pState->objException.ExceptionRecord.ExceptionRecord = 0;
					pState->objException.ExceptionRecord.NumberParameters = 0;
					memset( pState->objException.ExceptionRecord.ExceptionInformation, 0, sizeof( pState->objException.ExceptionRecord.ExceptionInformation ) );
					pState->objException.FirstChance = false;

					LoadTrapRecord( objControls, pqwBugCheckArguments[2] );
					fExceptionLoaded = true;
				}
				break;
		}

		// Note that we found a bug check record
		pState->fBugCheckDetected = true;
	}

	// Non-Bugcheck Analysis would go here, if we had any

	// And return the status
	return( fExceptionLoaded );
}

///
// Set the context record for the debugger given the address of a pointer in the debugger process memory
///
bool
LoadContextRecordFromPointer( const DEBUGGER_CONTROLS &objControls, ULONG64 offRecordPointer )
{
		ULONG64 offContextRecord;

		HRESULT dwReadResult = objControls.pDebugDataSpaces->ReadPointersVirtual( 1, offRecordPointer, &offContextRecord );

		if( dwReadResult == S_OK )
		{
			LoadContextRecord( objControls, offContextRecord );
			return( true );
		}
		else
		{
			return( false );
		}
}

///
// Load the context record given the record address in the debugger process memory
///
void 
LoadContextRecord( const DEBUGGER_CONTROLS &objControls, ULONG64 offContextRecord )
{
	WCHAR pwzCommand[MAX_PATH];
	_snwprintf_s( pwzCommand, MAX_PATH, _TRUNCATE, L".cxr 0x%I64x", offContextRecord );
	objControls.pDebugControl->ExecuteWide( DEBUG_OUTCTL_IGNORE, pwzCommand, DEBUG_EXECUTE_NOT_LOGGED );
}

///
// Set the trap record for the debugger given the address of a pointer in the debugger process memory
///
bool
LoadTrapRecordFromPointer( const DEBUGGER_CONTROLS &objControls, ULONG64 offRecordPointer )
{
	ULONG64 offTrapRecord;

	HRESULT dwReadResult = objControls.pDebugDataSpaces->ReadPointersVirtual( 1, offRecordPointer, &offTrapRecord );

	if( dwReadResult == S_OK )
	{
		LoadTrapRecord( objControls, offTrapRecord );
		return( true );
	}
	else
	{
		return( false );
	}
}

///
// Set the trap record for the debugger given the address of the trap record in the debugger process memory
///
void
LoadTrapRecord( const DEBUGGER_CONTROLS &objControls, ULONG64 offTrapRecord )
{
	WCHAR pwzCommand[MAX_PATH];
	_snwprintf_s( pwzCommand, MAX_PATH, _TRUNCATE, L".trap 0x%I64x", offTrapRecord );
	objControls.pDebugControl->ExecuteWide( DEBUG_OUTCTL_IGNORE, pwzCommand, DEBUG_EXECUTE_NOT_LOGGED );
}

///
/// Load an exception record into the state given the address of a pointer in the debugger process memory
///
bool
LoadExceptionRecordFromPointer( const DEBUGGER_CONTROLS &objControls, __in DEBUGGER_STATE *pState, ULONG64 offRecordPointer )
{
	ULONG64 offExceptionRecord;

	HRESULT dwReadResult = objControls.pDebugDataSpaces->ReadPointersVirtual( 1, offRecordPointer, &offExceptionRecord );

	if( dwReadResult == S_OK )
	{
		return( LoadExceptionRecord( objControls, pState, offExceptionRecord ) );
	}
	else
	{
		return( false );
	}
}

///
/// Load an exception record into the state given the address of the record in the debugger process memory
///
bool
LoadExceptionRecord( const DEBUGGER_CONTROLS &objControls, __in DEBUGGER_STATE *pState, ULONG64 offExceptionRecord )
{
	if( pState->dwProcessor == IMAGE_FILE_MACHINE_I386 )
	{
		EXCEPTION_RECORD32 objException;
		ULONG dwSizeRead;

		DWORD dwReadResult = objControls.pDebugDataSpaces->ReadVirtual( offExceptionRecord, &objException, sizeof( EXCEPTION_RECORD32 ), &dwSizeRead );

		if( (dwReadResult == S_OK) && (dwSizeRead == sizeof( EXCEPTION_RECORD32 ) ) )
		{
			pState->objException.FirstChance = false;

			pState->objException.ExceptionRecord.ExceptionAddress = objException.ExceptionAddress;
			pState->objException.ExceptionRecord.ExceptionCode = objException.ExceptionCode;
			pState->objException.ExceptionRecord.ExceptionFlags = objException.ExceptionFlags;
			pState->objException.ExceptionRecord.ExceptionRecord = objException.ExceptionRecord;
			pState->objException.ExceptionRecord.NumberParameters = objException.NumberParameters;

			for( DWORD iIndex = 0; iIndex < EXCEPTION_MAXIMUM_PARAMETERS; iIndex++ )
			{
				pState->objException.ExceptionRecord.ExceptionInformation[iIndex] = objException.ExceptionInformation[iIndex];
			}
		}
		else
		{
			return( false );
		}
	}
	else if( pState->dwProcessor == IMAGE_FILE_MACHINE_AMD64 )
	{
		EXCEPTION_RECORD64 objException;
		ULONG dwSizeRead;

		DWORD dwReadResult = objControls.pDebugDataSpaces->ReadVirtual( offExceptionRecord, &objException, sizeof( EXCEPTION_RECORD64 ), &dwSizeRead );

		if( (dwReadResult == S_OK) && (dwSizeRead == sizeof( EXCEPTION_RECORD64 ) ) )
		{
			pState->objException.FirstChance = false;
			memcpy( &pState->objException.ExceptionRecord, &objException, sizeof( objException ) );
		}
		else
		{
			return( false );
		}
	}
	else
	{
		return( false );
	}

	return( true );
}

///
// Check the exception chain looking for evidence of a broken exception handler chain
///
bool
GatherExceptionChainInformation( const DEBUGGER_CONTROLS &objControls, __in DEBUGGER_STATE *pState )
{
	
	OUTPUT_MONITOR *pMonitor = new OUTPUT_MONITOR();  //pMonitor handles its own cleanup by calling delete on the this ptr
	PDEBUG_OUTPUT_CALLBACKS_WIDE pPreviousCallback = NULL;

	if (pMonitor == NULL){
		return false;
	}

	// We need to hook the output information so that we can monitor the results of the
	// !exchain command
	objControls.pDebugClient->GetOutputCallbacksWide( &pPreviousCallback );
	objControls.pDebugClient->SetOutputCallbacksWide( pMonitor );

	// Execute the command
	objControls.pDebugControl->ExecuteWide( DEBUG_OUTCTL_THIS_CLIENT | DEBUG_OUTCTL_NOT_LOGGED, L"!exchain", DEBUG_EXECUTE_NOT_LOGGED | DEBUG_EXECUTE_NO_REPEAT );

	// Analyze the output
	LPCWSTR *ppwzOutputText = pMonitor->GetOutputText();

	for( DWORD iEntry = 0; !pState->fExceptionChainCorruptionDetected && iEntry < pMonitor->GetOutputTextCount(); iEntry++ )
	{
		if( wcsstr( ppwzOutputText[iEntry], L"Invalid exception" ) != NULL )
		{
			// Handle a false positive in !exchain
			if( (wcsstr( ppwzOutputText[iEntry], L"Invalid exception stack at ffffffff" ) == NULL) &&
				(wcsstr( ppwzOutputText[iEntry], L"Invalid exception stack at 00000000ffffffff") == NULL) )
			{
				pState->fExceptionChainCorruptionDetected = true;
			}
		}
	}

	// Restore the old output
	objControls.pDebugClient->SetOutputCallbacksWide( pPreviousCallback );

	return( true );
}

///
// Get the address where TEB32 is located by calling !TEB
// looks for message  'Wow64 TEB32 at xxxxxxxxxxxxxxxx\n'
// when found converts the address represented in the x's
///
bool GatherTEB32Address( const DEBUGGER_CONTROLS &objControls, PULONG64  pTEB32RecordAddress)
{
	OUTPUT_MONITOR *pMonitor = new OUTPUT_MONITOR();  //pMonitor handles its own cleanup by calling delete on the this ptr
	PDEBUG_OUTPUT_CALLBACKS_WIDE pPreviousCallback = NULL;
	WCHAR mesageSigniture [] = L"Wow64 TEB32 at ";
	ULONG offTEB32Record =0;
	bool success = false;


	if (pMonitor == NULL){
		return false;
	}


	// We need to hook the output information so that we can monitor the results of the
	// !teb command
	objControls.pDebugClient->GetOutputCallbacksWide( &pPreviousCallback );
	objControls.pDebugClient->SetOutputCallbacksWide( pMonitor );

	// Execute the command
	objControls.pDebugControl->ExecuteWide( DEBUG_OUTCTL_THIS_CLIENT | DEBUG_OUTCTL_NOT_LOGGED, L"!teb", DEBUG_EXECUTE_NOT_LOGGED | DEBUG_EXECUTE_NO_REPEAT );

	// Analyze the output
	LPCWSTR *ppwzOutputText = pMonitor->GetOutputText();

	for( DWORD iEntry = 0; iEntry < pMonitor->GetOutputTextCount(); iEntry++ )
	{
		LPCWSTR index = wcsstr( ppwzOutputText[iEntry],mesageSigniture );
		if( index!= NULL )
		{
			index += (_countof(mesageSigniture) -1); // -1 is due to NULL in the const array
			
			if (wcslen(index) == 17) // after the intial message there should be 17 chars left, 16 for hex address and \n
			{
				LPWSTR endIndex = (LPWSTR)index + 16; //end pointer  after 16 char address	
				offTEB32Record =(ULONG64) wcstoul(index,&endIndex,16); //

				if (offTEB32Record != ULONG_MAX && offTEB32Record != 0 && errno != ERANGE)
				{
					*pTEB32RecordAddress = offTEB32Record;
					success = true;
					break;
				}
			}
		}
	}

	// Restore the old output
	objControls.pDebugClient->SetOutputCallbacksWide( pPreviousCallback );

	return( success );
}

///
// Clear the existing stack information. This is used to invalidate the stack if the context
// is changed
///
void
ClearStackInformation( __in DEBUGGER_STATE *pState ) 
{
	// Flush old stack information, if any
	if( pState->pStack )
	{
		delete[] pState->pStack;
		pState->pStack = NULL;
	}

	if( pState->pfExcludedStackFrames )
	{
		delete[] pState->pfExcludedStackFrames;
		pState->pfExcludedStackFrames = NULL;
	}

	pState->dwMajorHash = 0;
	pState->dwMinorHash = 0;
	pState->fStackContainsUnknownSymbols = false;
	pState->cStackFrames = 0;
	pState->iInvokingStackFrame = -1;
	pState->fStackContainsUnknownSymbols = false;
	wcscpy_s( pState->pwzFaultingLocation, FAULTING_LOCATION_STRING_LENGTH, L"Undetermined Location" );
	pState->fSourceInformationAvailable = false;
	wcscpy_s( pState->pwzFaultingFile, FAULTING_FILE_STRING_LENGTH, L"No Source File Found" );
	pState->dwFaultingLine = 0;

}

/// Get the stack information, and calculate the hash information based on the stack
bool 
GatherStackInformation( const DEBUGGER_CONTROLS &objControls, __in DEBUGGER_STATE *pState )
{
	// Abort out if we already have stack information
	if( pState->pStack )
	{		
		return( true );
	}

	// Gather the stack information
	pState->pStack = new DEBUG_STACK_FRAME[ANALYSIS_STACK_DEPTH];
	
	if (objControls.pDebugControl->GetStackTrace( 0, 0, 0, pState->pStack, (ULONG)ANALYSIS_STACK_DEPTH, (PULONG)&pState->cStackFrames) != S_OK)
	{
		delete[] pState->pStack;
		pState->pStack = NULL;
		pState->cStackFrames = 0;

		Log( objControls.pDebugControl, MACHINE, L"ERROR:NO_STACK_INFORMATION\n" );
		Log( objControls.pDebugControl, TERSE, HUMAN, L"Error: Unable to extract the stack information from the debugger.\n" );
		return( false );
	}
	
	pState->pfExcludedStackFrames = new bool[pState->cStackFrames];
	pState->pfUnknownStackFrames = new bool[pState->cStackFrames];

	if( (pState->pfExcludedStackFrames == NULL) || (pState->pfUnknownStackFrames == NULL) )
	{
			Log( objControls.pDebugControl, MACHINE, L"ERROR:STACK_INFORMATION_ALLOCATION_FAILURE\n" );
			Log( objControls.pDebugControl, TERSE, HUMAN, L"Error: Unable to allocate the memory for stack analysis.\n" );
			return( false );
	}

	CalculateStackHash( objControls, pState->cStackFrames, pState->pStack,s_eHashingMode, pState->pfUnknownStackFrames, pState->pfExcludedStackFrames, &pState->fStackContainsUnknownSymbols, &pState->dwMajorHash, &pState->dwMinorHash );

	if( pState->fExceptionLoaded )
	{
		SetFaultingInstructionInformation( objControls, pState, pState->objException.ExceptionRecord.ExceptionAddress );
	}
	else
	{
		if(pState->cStackFrames == 0){
			Log( objControls.pDebugControl, MACHINE, L"ERROR:STACK_INFORMATION_POPULATION_FAILURE\n" );
			Log( objControls.pDebugControl, TERSE, HUMAN, L"Error: Unable to populate the stack information.\n" );
			return( false );
		}
		SetFaultingInstructionInformation( objControls, pState, pState->pStack[0].InstructionOffset );			 
	}

	// And get and cache the thread stack memory locations.
	//
	// The TEB starts with the NT_TIB, the second and third fields of which are the stack ranges
	//
	// Documentation on this can be found at http://msdn.microsoft.com/en-us/library/cc267849.aspx
	ULONG64 offTEB;

	if( objControls.pSystemObjects->GetCurrentThreadTeb( &offTEB ) == S_OK )
	{
		ULONG64 offPointerSize;
		ULONG64 arrStackRangeOffsets[2];

		// Determine the pointer offset for supported processors. If we don't have a supported processor
		// go ahead and return out
		switch( pState->dwProcessor )
		{
			case IMAGE_FILE_MACHINE_I386:
			case IMAGE_FILE_MACHINE_ARM:
			case IMAGE_FILE_MACHINE_THUMB:
			case IMAGE_FILE_MACHINE_ARMNT:
				offPointerSize = 4;
				break;

			case IMAGE_FILE_MACHINE_AMD64:
				offPointerSize = 8;
				break;

			default:
				return( true );
		}


		
		if( pState->dwProcessor != pState->dwActualProcessor ) // got to get 32 bit teb becuase we are running in WOW mode
		{
			GatherTEB32Address(objControls,&offTEB);
		}

		if( objControls.pDebugDataSpaces->ReadPointersVirtual( 2, offTEB + offPointerSize, arrStackRangeOffsets ) == S_OK )
		{
			pState->offThreadStackBase = arrStackRangeOffsets[0];
			pState->offThreadStackLimit = arrStackRangeOffsets[1];
		}
		else
		{
			// Issue a warning, but don't cancel out if we cannot read from the TEB
			Log( objControls.pDebugControl, MACHINE, L"WARNING:TEB_UNREADABLE\n" );
			Log( objControls.pDebugControl, TERSE, HUMAN, L"Warning: Unable to read from the TEB in the current thread.\n" );
		}
	}
	else
	{
		// Issue a warning, but don't cancel out if we cannot isolate the TEB
		Log( objControls.pDebugControl, MACHINE, L"WARNING:NO_TEB_FOUND\n" );
		Log( objControls.pDebugControl, TERSE, HUMAN, L"Warning: Unable to find the TEB in the current thread.\n" );
	}

	return( true );
}

///
// Update the information on the faulting instruction 
void
SetFaultingInstructionInformation( const DEBUGGER_CONTROLS &objControls, __in DEBUGGER_STATE *pState, UINT64 offInstruction )
{	
	// Walk the stack for non-excluding symbols, and if we find excluded symbols, we walk futher back to assign
	// an invoker
	pState->offInstruction = offInstruction;
	pState->iInvokingStackFrame = 0;
	
	for( DWORD iFrame = 0; iFrame < pState->cStackFrames - 1; iFrame++ )
	{
		if( offInstruction == pState->pStack[iFrame].InstructionOffset )
		{
			if( pState->pfExcludedStackFrames[iFrame] || pState->pfUnknownStackFrames[iFrame] )
			{
				offInstruction = pState->pStack[iFrame+1].InstructionOffset;
				pState->iInvokingStackFrame = (LONG) (iFrame + 1);
			}
			else
			{
				break;
			}
		}
	}
			 
	// Gather the fault location symbol
	ULONG64 offDisplacement;
	WCHAR pwzSymbol[128];
	WCHAR pwzFaultingInstruction[FAULTING_LOCATION_STRING_LENGTH];
	pState->fSourceInformationAvailable = false;

	HRESULT dwResult = objControls.pDebugSymbols->GetNameByOffsetWide( pState->offInstruction, pwzSymbol , 128, NULL, (PULONG64)&offDisplacement);
	
	
	if( dwResult != E_FAIL )
	{
		_snwprintf_s( pwzFaultingInstruction, FAULTING_LOCATION_STRING_LENGTH, _TRUNCATE, L"%s+0x%16.16I64x", pwzSymbol, offDisplacement );
	}
	else
	{
		_snwprintf_s( pwzFaultingInstruction, FAULTING_LOCATION_STRING_LENGTH, _TRUNCATE, L"Unknown Symbol @ 0x%16.16I64x", pState->offInstruction );
	}

	if( pState->iInvokingStackFrame == 0 )
	{
		wcscpy_s( pState->pwzFaultingLocation, pwzFaultingInstruction );
		
		dwResult = objControls.pDebugSymbols->GetLineByOffsetWide( pState->offInstruction, &pState->dwFaultingLine, pState->pwzFaultingFile, FAULTING_FILE_STRING_LENGTH, NULL, NULL );
		pState->fSourceInformationAvailable = dwResult != E_FAIL;
	}
	else
	{
		dwResult = objControls.pDebugSymbols->GetNameByOffsetWide( pState->pStack[pState->iInvokingStackFrame].InstructionOffset, pwzSymbol, 128, NULL, (PULONG64) &offDisplacement );

		if( dwResult != E_FAIL )
		{
			_snwprintf_s( pState->pwzFaultingLocation, FAULTING_LOCATION_STRING_LENGTH, _TRUNCATE, L"%s called from %s+0x%16.16I64x", pwzFaultingInstruction, pwzSymbol, offDisplacement );
			
			dwResult = objControls.pDebugSymbols->GetLineByOffsetWide( pState->pStack[pState->iInvokingStackFrame].InstructionOffset, &pState->dwFaultingLine, pState->pwzFaultingFile, FAULTING_FILE_STRING_LENGTH, NULL, NULL );
			pState->fSourceInformationAvailable = dwResult != E_FAIL;
		}
		else
		{
			_snwprintf_s( pState->pwzFaultingLocation, FAULTING_LOCATION_STRING_LENGTH, _TRUNCATE, L"%s called from Unknown Symbol @ 0x%16.16I64x", pwzFaultingInstruction, pState->pStack[pState->iInvokingStackFrame].InstructionOffset );
		}
	}

	// Note that if we failed anywhere along the line in getting the information, we'd have saved that information
	// here and can go ahead and do the override once
	if( !pState->fSourceInformationAvailable )
	{
		pState->dwFaultingLine = 0;
		wcscpy_s( pState->pwzFaultingFile, FAULTING_FILE_STRING_LENGTH, L"No Source File Found" );
	}
}

///
// Get the individual flags from the debugger
bool
GatherProcessorFlags( const DEBUGGER_CONTROLS &objControls, __in DEBUGGER_STATE *pState )
{
	if( GetProcessorFlagByName(objControls, L"cf" ) )
	{
		AddOperandToSet( CARRY_FLAG, &pState->setProcessorFlags );
	}

	if( GetProcessorFlagByName( objControls, L"zf" ) )
	{
		AddOperandToSet( ZERO_FLAG, &pState->setProcessorFlags );
	}

	if( GetProcessorFlagByName( objControls, L"of" ) || GetProcessorFlagByName( objControls, L"vf" ) )
	{
		AddOperandToSet( OVERFLOW_FLAG, &pState->setProcessorFlags );
	}

	if( GetProcessorFlagByName( objControls, L"af" ) )
	{
		AddOperandToSet( AUX_FLAG, &pState->setProcessorFlags );
	}

	if( GetProcessorFlagByName( objControls, L"pf" ) )
	{
		AddOperandToSet( PARITY_FLAG, &pState->setProcessorFlags );
	}

	if( GetProcessorFlagByName( objControls, L"sf" ) || GetProcessorFlagByName( objControls, L"nf" ) )
	{
		AddOperandToSet( SIGN_FLAG, &pState->setProcessorFlags );
	}

	return( true );
}

///
// Disassemble the faulting instruction
bool
GatherFaultingInstruction( const DEBUGGER_CONTROLS &objControls, __in DEBUGGER_STATE *pState )
{
	INSTRUCTION *pInstruction = new INSTRUCTION;	
	// The flags register is only valid if we are starting our analysis from the actual root of the stack frame. If we have walked
	// backwards or otherwise done inference to find the faulting instruction, then we cannot presume the flags register is valid.
	//
	// Note that for exceptions, we will have loaded the context of the exception and regathered the stack, so this case shuld still
	// be met in many cases.
	bool fFlagsRegisterValid = (pState->cStackFrames > 0) && (pState->offInstruction == pState->pStack[0].InstructionOffset);

	// If we have a valid flags register at the start, go ahead and actually gather the information on what the flags are
	if( fFlagsRegisterValid )
	{
		bool fError = !GatherProcessorFlags( objControls, pState );

		if( fError )
		{
			return( false );
		}
	}	
	bool fResult = Disassemble( objControls, pState->offInstruction, pState->dwProcessor, fFlagsRegisterValid, pState->setProcessorFlags, pInstruction );

	if (!fResult)
		pInstruction->eClass = NONDISASSEMBLE_INSTRUCTION; 

	pState->objSourceCode.push_front( pInstruction );

	return( true );
}

///
// Determine whether or not the exception needs to be overriden based on the information available from the
// faulting instruction
bool GatherExceptionOverridesBasedOnFaultingInstruction( const DEBUGGER_CONTROLS &, __in DEBUGGER_STATE *pState )
{	
	// Override case:
	//
	// Input: A PUSH operation causes a write access violation on the stack guard page
	// Transformation: Turn the exception into a STATUS_STACK_OVERFLOW
	if( (pState->objSourceCode.front()->eClass == STACK_PUSH) &&
		pState->fExceptionLoaded &&
		((pState->objException.ExceptionRecord.ExceptionCode & 0xffffffff) == STATUS_ACCESS_VIOLATION) &&
		(pState->objException.ExceptionRecord.ExceptionInformation[0] == ACCESS_VIOLATION_TYPE_WRITE) &&
		((pState->offFaultingAddress < pState->offThreadStackLimit) && ((pState->offFaultingAddress + 0x40) >= pState->offThreadStackLimit)) )
	{
		pState->objException.ExceptionRecord.ExceptionCode = STATUS_STACK_OVERFLOW;		
	}

	return( true );
}

///
// Disassemble the basic block and gather the taint tracking information
bool
GatherBasicBlock( const DEBUGGER_CONTROLS &objControls, __in DEBUGGER_STATE *pState )
{
	// Guarantee that the initial instruction has been determined, even if it wasn't included in the rules
	if( pState->objSourceCode.empty() )
	{
		bool fError = !GatherFaultingInstruction( objControls, pState );

		if( fError )
		{
			return( false );
		}
	}

	INSTRUCTION *pPreviousInstruction = pState->objSourceCode.back();
	
	while( (pPreviousInstruction->eClass != BRANCH) && (pPreviousInstruction->eClass != RETURN) &&(pPreviousInstruction->eClass != INTERRUPT) && (pPreviousInstruction->eClass != UNPREDICTABLE_CONDITIONAL_EXECUTION ) )
	{
		INSTRUCTION *pNextInstruction = new INSTRUCTION;

		bool fResult = Disassemble( objControls,
									pPreviousInstruction->offNextInstruction,
									pState->dwProcessor,
									pPreviousInstruction->fFlagsRegisterValid && !pPreviousInstruction->fFlagsRegisterModified,
									pState->setProcessorFlags,
									pNextInstruction );

		if( fResult )
		{
			pState->objSourceCode.push_back( pNextInstruction );
			pPreviousInstruction = pNextInstruction;
		}
		else
		{
			// We cannot disassemble any further, end
			delete pNextInstruction;
			pNextInstruction = NULL;

			return( true );
		}
	}

	return( true );
}


///
// Perform the taint tracking for the basic block
bool
GatherTaintInformation( const DEBUGGER_CONTROLS &, __in DEBUGGER_STATE *pState )
{
	return( AssignTaint( pState->dwProcessor,
		    ((pState->objException.ExceptionRecord.ExceptionCode & 0xffffffff) == STATUS_ACCESS_VIOLATION) && (pState->objException.ExceptionRecord.ExceptionInformation[0] == ACCESS_VIOLATION_TYPE_WRITE),
	 	    &pState->objSourceCode ) );
}

//////////////////////////////////////////////////////////////////////////////////////////////////////
// Report Procedures
//////////////////////////////////////////////////////////////////////////////////////////////////////

///
/// Log the initial state to the debugger
///
void 
ReportInitialState( const DEBUGGER_CONTROLS &objControls, const DEBUGGER_STATE &objState )
{
	if( objState.pwzIdentity != NULL )
	{
		Log( objControls.pDebugControl, MACHINE, L"IDENTITY:%s\n", objState.pwzIdentity );
		Log( objControls.pDebugControl, VERBOSE, HUMAN, L"%s\n", objState.pwzIdentity );
	}

	switch( objState.dwProcessor )
	{
		case IMAGE_FILE_MACHINE_I386:
			Log( objControls.pDebugControl, MACHINE, L"PROCESSOR:X86\n" );
			Log( objControls.pDebugControl, VERBOSE, HUMAN, L"Executing Processor Architecture is x86\n" );
			break;

		case IMAGE_FILE_MACHINE_ARM:
			Log( objControls.pDebugControl, MACHINE, L"PROCESSOR:ARM\n" );
			Log( objControls.pDebugControl, VERBOSE, HUMAN, L"Executing Processor Architecture is ARM\n" );
			break;

		case IMAGE_FILE_MACHINE_IA64:
			Log( objControls.pDebugControl, MACHINE, L"PROCESSOR:IA64\n" );
			Log( objControls.pDebugControl, VERBOSE, HUMAN, L"Executing Processor Architecture is Itanium\n" );
			break;

		case IMAGE_FILE_MACHINE_AMD64:
			Log( objControls.pDebugControl, MACHINE, L"PROCESSOR:X64\n" );
			Log( objControls.pDebugControl, VERBOSE, HUMAN, L"Executing Processor Architecture is x64\n" );
			break;

		case IMAGE_FILE_MACHINE_EBC:
			Log( objControls.pDebugControl, MACHINE, L"PROCESSOR:EBC\n" );
			Log( objControls.pDebugControl, VERBOSE, HUMAN, L"Executing Processor Architecture is EBC\n" );
			break;

		default:
			Log( objControls.pDebugControl, MACHINE, L"PROCESSOR:UNKNOWN\n" );
			Log( objControls.pDebugControl, VERBOSE, HUMAN, L"Executing Processor Architecture could not be determined\n" );
			break;

	}

	switch (objState.dwDebugClass)
	{
		case DEBUG_CLASS_KERNEL:
			Log( objControls.pDebugControl, MACHINE, L"CLASS:KERNEL\n" );
			Log( objControls.pDebugControl, VERBOSE, HUMAN, L"Debuggee is in Kernel Mode\n" );

			switch (objState.dwDebugQualifier)
			{
				case DEBUG_KERNEL_LOCAL:
					Log( objControls.pDebugControl, MACHINE, L"QUALIFIER:KERNEL_PROCESS\n" );
					Log( objControls.pDebugControl, VERBOSE, HUMAN, L"Debuggee is a live kernel debugging session on the local machine\n" );
					break;

				case DEBUG_KERNEL_CONNECTION:
				case DEBUG_KERNEL_EXDI_DRIVER:
					Log( objControls.pDebugControl, MACHINE, L"QUALIFIER:KERNEL_PROCESS_REMOTE\n" );
					Log( objControls.pDebugControl, VERBOSE, HUMAN, L"Debuggee is a live kernel mode debugging session on a remote machine\n" );
					break;


				case DEBUG_KERNEL_SMALL_DUMP:
					Log( objControls.pDebugControl, MACHINE, L"QUALIFIER:KERNEL_SMALL_DUMP\n" );
					Log( objControls.pDebugControl, VERBOSE, HUMAN, L"Debuggee is a kernel mode small dump file\n" );
					break;

				case DEBUG_KERNEL_DUMP:
					Log( objControls.pDebugControl, MACHINE, L"QUALIFIER:KERNEL_DUMP\n" );
					Log( objControls.pDebugControl, VERBOSE, HUMAN, L"Debuggee is a kernel mode dump file\n" );
					break;

				case DEBUG_KERNEL_FULL_DUMP:
					Log( objControls.pDebugControl, MACHINE, L"QUALIFIER:KERNEL_FULL_DUMP\n" );
					Log( objControls.pDebugControl, VERBOSE, HUMAN, L"Debuggee is a kernel mode full dump file\n" );
					break;
			}
			break;

		case DEBUG_CLASS_USER_WINDOWS:
			Log( objControls.pDebugControl, MACHINE, L"CLASS:USER\n" );
			Log( objControls.pDebugControl, VERBOSE, HUMAN, L"Debuggee is in User Mode\n" );

			switch (objState.dwDebugQualifier)
			{
				case DEBUG_USER_WINDOWS_PROCESS:
					Log( objControls.pDebugControl, MACHINE, L"QUALIFIER:USER_PROCESS\n" );
					Log( objControls.pDebugControl, VERBOSE, HUMAN, L"Debuggee is a live user mode debugging session on the local machine\n" );
					break;

				case DEBUG_USER_WINDOWS_PROCESS_SERVER:
					Log( objControls.pDebugControl, MACHINE, L"QUALIFIER:USER_PROCESS_REMOTE\n" );
					Log( objControls.pDebugControl, VERBOSE, HUMAN, L"Debuggee is a live user mode debugging session on a remote machine\n" );
					break;

				case DEBUG_USER_WINDOWS_SMALL_DUMP:
					Log( objControls.pDebugControl, MACHINE, L"QUALIFIER:USER_SMALL_DUMP\n" );
					Log( objControls.pDebugControl, VERBOSE, HUMAN, L"Debuggee is a user mode small dump file\n" );
					break;

				case DEBUG_USER_WINDOWS_DUMP:
					Log( objControls.pDebugControl, MACHINE, L"QUALIFIER:USER_DUMP\n" );
					Log( objControls.pDebugControl, VERBOSE, HUMAN, L"Debuggee is a user mode dump file\n" );
					break;
			}
			break;

		case DEBUG_CLASS_UNINITIALIZED:
			Log( objControls.pDebugControl, MACHINE, L"CLASS:UNINITIALIZED\n" );
			Log( objControls.pDebugControl, VERBOSE, HUMAN, L"Debuggee is in an Uninitialized Mode\n" );
			break;
	}
	
	switch( objState.dwEventType )
	{
		case DEBUG_EVENT_BREAKPOINT:
			Log( objControls.pDebugControl, MACHINE, L"EVENT:DEBUG_EVENT_BREAKPOINT\n" );
			Log( objControls.pDebugControl, VERBOSE, HUMAN, L"Event Type: Breakpoint\n" );
			break;

		case DEBUG_EVENT_EXCEPTION:
			Log( objControls.pDebugControl, MACHINE, L"EVENT:DEBUG_EVENT_EXCEPTION\n" );
			Log( objControls.pDebugControl, VERBOSE, HUMAN, L"Event Type: Exception\n" );
			break;

		case DEBUG_EVENT_CREATE_THREAD:
			Log( objControls.pDebugControl, MACHINE, L"EVENT:DEBUG_EVENT_CREATE_THREAD\n" );
			Log( objControls.pDebugControl, VERBOSE, HUMAN, L"Event Type: Thread Creation\n" );
			break;

		case DEBUG_EVENT_EXIT_THREAD:
			Log( objControls.pDebugControl, MACHINE, L"EVENT:DEBUG_EVENT_EXIT_THREAD\n" );
			Log( objControls.pDebugControl, VERBOSE, HUMAN, L"Event Type: Thread Exit\n" );
			break;

		case DEBUG_EVENT_CREATE_PROCESS:
			Log( objControls.pDebugControl, MACHINE, L"EVENT:DEBUG_EVENT_CREATE_PROCESS\n" );
			Log( objControls.pDebugControl, VERBOSE, HUMAN, L"Event Type: Process Created\n" );
			break;

		case DEBUG_EVENT_EXIT_PROCESS:
			Log( objControls.pDebugControl, MACHINE, L"EVENT:DEBUG_EVENT_EXIT_PROCESS\n" );
			Log( objControls.pDebugControl, VERBOSE, HUMAN, L"Event Type: Process Ended\n" );
			break;

		case DEBUG_EVENT_LOAD_MODULE:
			Log( objControls.pDebugControl, MACHINE, L"EVENT:DEBUG_EVENT_LOAD_MODULE\n" );
			Log( objControls.pDebugControl, VERBOSE, HUMAN, L"Event Type: Module Load\n" );
			break;

		case DEBUG_EVENT_UNLOAD_MODULE:
			Log( objControls.pDebugControl, MACHINE, L"EVENT:DEBUG_EVENT_UNLOAD_MODULE\n" );
			Log( objControls.pDebugControl, VERBOSE, HUMAN, L"Event Type: Module Unload\n" );
			break;

		case DEBUG_EVENT_SYSTEM_ERROR:
			Log( objControls.pDebugControl, MACHINE, L"EVENT:DEBUG_EVENT_SYSTEM_ERROR\n" );
			Log( objControls.pDebugControl, VERBOSE, HUMAN, L"Event Type: System Error\n" );
			break;

	}
}

///
/// Log the exception classification
///
void
ReportExceptionInformation( const DEBUGGER_CONTROLS &objControls, const DEBUGGER_STATE &objState )
{
	// Log the exception information
	WCHAR *pwzTrapName = L"UNKNOWN";

	for (int iTrap = 0; TRAP_DECODE_MAP[iTrap].name != NULL; iTrap++)
	{
		if (TRAP_DECODE_MAP[iTrap].code == (objState.objException.ExceptionRecord.ExceptionCode & 0xffffffff))
		{
			pwzTrapName = TRAP_DECODE_MAP[iTrap].name;
			break;
		}
	}

	Log( objControls.pDebugControl, MACHINE, L"EXCEPTION_FAULTING_ADDRESS:0x%I64x\n", objState.offFaultingAddress );
	Log( objControls.pDebugControl, VERBOSE, HUMAN, L"Exception Faulting Address: 0x%I64x\n", objState.offFaultingAddress );

	Log( objControls.pDebugControl, MACHINE, L"EXCEPTION_CODE:0x%X\n", (DWORD) (objState.objException.ExceptionRecord.ExceptionCode & 0xffffffff) );
	Log( objControls.pDebugControl, MACHINE, L"EXCEPTION_LEVEL:%s\n", objState.objException.FirstChance ? L"FIRST_CHANCE" : L"SECOND_CHANCE" );
	Log( objControls.pDebugControl, VERBOSE, HUMAN, L"%s Exception Type: %s (0x%X)\n", 
												  objState.objException.FirstChance ? L"First Chance" : L"Second Chance",
												  pwzTrapName,
												  (DWORD) (objState.objException.ExceptionRecord.ExceptionCode & 0xffffffff) );
	
	Log( objControls.pDebugControl, MACHINE, L"EXCEPTION_TYPE:%s\n", pwzTrapName );

	if( (objState.objException.ExceptionRecord.ExceptionCode & 0xffffffff) == STATUS_ACCESS_VIOLATION )
	{
		switch( objState.objException.ExceptionRecord.ExceptionInformation[0] )
		{
			case ACCESS_VIOLATION_TYPE_WRITE:
				Log( objControls.pDebugControl, MACHINE, L"EXCEPTION_SUBTYPE:WRITE\n" );
				Log( objControls.pDebugControl, VERBOSE, HUMAN, L"Exception Sub-Type: Write Access Violation\n" );
				break;

			case ACCESS_VIOLATION_TYPE_READ:
				Log( objControls.pDebugControl, MACHINE, L"EXCEPTION_SUBTYPE:READ\n" );
				Log( objControls.pDebugControl, VERBOSE, HUMAN, L"Exception Sub-Type: Read Access Violation\n" );
				break;

			case ACCESS_VIOLATION_TYPE_DEP:
				Log( objControls.pDebugControl, MACHINE, L"EXCEPTION_SUBTYPE:DEP\n" );
				Log( objControls.pDebugControl, VERBOSE, HUMAN, L"Exception Sub-Type: Data Execution Protection (DEP) Violation\n" );
				break;
		}
	}
}

///
/// Report the analysis of the faulting instruction
///
void
ReportFaultingInstruction( const DEBUGGER_CONTROLS &objControls, const DEBUGGER_STATE &objState )
{
	Log( objControls.pDebugControl, MACHINE, L"FAULTING_INSTRUCTION:%s %s %s\n", objState.objSourceCode.front()->pwzAddress, objState.objSourceCode.front()->pwzMnemonic, objState.objSourceCode.front()->pwzArguments );
	Log( objControls.pDebugControl, VERBOSE, HUMAN, L"\nFaulting Instruction:%s %s %s\n", objState.objSourceCode.front()->pwzAddress, objState.objSourceCode.front()->pwzMnemonic, objState.objSourceCode.front()->pwzArguments );
}

///
/// Report the analysis of the basic block
///
void
ReportBasicBlock( const DEBUGGER_CONTROLS &objControls, const DEBUGGER_STATE &objState )
{
	INSTRUCTION_LIST::const_iterator itInstruction = objState.objSourceCode.begin();

	Log( objControls.pDebugControl, VERBOSE, HUMAN, L"\nBasic Block:\n" );
	Log( objControls.pDebugControl, MACHINE, L"BASIC_BLOCK_INSTRUCTION_COUNT:%u\n", objState.objSourceCode.size() );

	while( itInstruction != objState.objSourceCode.end() )
	{
		INSTRUCTION *pInstruction = *itInstruction;

		Log( objControls.pDebugControl, VERBOSE, HUMAN, L"    %s %s %s\n", pInstruction->pwzAddress, pInstruction->pwzMnemonic, pInstruction->pwzArguments );
		Log( objControls.pDebugControl, MACHINE, L"BASIC_BLOCK_INSTRUCTION:%s %s %s\n", pInstruction->pwzAddress, pInstruction->pwzMnemonic, pInstruction->pwzArguments );
		LogOperandSet( objControls.pDebugControl, L"Tainted Input operands", L"BASIC_BLOCK_INSTRUCTION_TAINTED_INPUT_OPERAND", pInstruction->setTaintedInputRegisters );

		itInstruction++;
	}
}

///
/// Report the full analysis of the basic block
///
void
ReportBasicBlockDetails( const DEBUGGER_CONTROLS &objControls, const DEBUGGER_STATE &objState )
{
	INSTRUCTION_LIST::const_iterator itInstruction = objState.objSourceCode.begin();
	bool fProcessorFlagsDisplayed = false;

	Log( objControls.pDebugControl, VERBOSE, HUMAN, L"\nBasic Block:\n" );
	Log( objControls.pDebugControl, MACHINE, L"BASIC_BLOCK_INSTRUCTION_COUNT:%u\n", objState.objSourceCode.size() );

	while( itInstruction != objState.objSourceCode.end() )
	{
		INSTRUCTION *pInstruction = *itInstruction;

		if( pInstruction->fFlagsRegisterValid && !fProcessorFlagsDisplayed  )
		{
			LogOperandSet( objControls.pDebugControl, L"Processor Flags", L"BASIC_BLOCK_PROCESSOR_FLAGS", objState.setProcessorFlags );
			fProcessorFlagsDisplayed = true;
		}

		Log( objControls.pDebugControl, VERBOSE, HUMAN, L"    %s %s %s\n", pInstruction->pwzAddress, pInstruction->pwzMnemonic, pInstruction->pwzArguments );
		Log( objControls.pDebugControl, MACHINE, L"BASIC_BLOCK_INSTRUCTION:%s %s %s\n", pInstruction->pwzAddress, pInstruction->pwzMnemonic, pInstruction->pwzArguments );
		Log( objControls.pDebugControl, VERBOSE, HUMAN, L"    Metainstruction Classification: %s\n", METAINSTRUCTION_TABLE[pInstruction->eClass] );
		Log( objControls.pDebugControl, MACHINE, L"BASIC_BLOCK_METAINSTRUCTION:%s\n", METAINSTRUCTION_TABLE[pInstruction->eClass] );
		LogOperandSet( objControls.pDebugControl, L"Tainted Input operands", L"BASIC_BLOCK_INSTRUCTION_TAINTED_INPUT_OPERAND", pInstruction->setTaintedInputRegisters );
		LogOperandSet( objControls.pDebugControl, L"Explicit Operands", L"BASIC_BLOCK_INSTRUCTION_EXPLICIT_OPERANDS", pInstruction->setExplicitRegisters );
		LogOperandSet( objControls.pDebugControl, L"Source Operands", L"BASIC_BLOCK_INSTRUCTION_SOURCE_OPERANDS", pInstruction->setSourceRegisters );
		LogOperandSet( objControls.pDebugControl, L"Destination Operands", L"BASIC_BLOCK_INSTRUCTION_DESTINATION_OPERANDS", pInstruction->setDestinationRegisters );
		LogOperandSet( objControls.pDebugControl, L"Destination Pointer Operands", L"BASIC_BLOCK_INSTRUCTION_DESTINATION_POINTER_OPERANDS", pInstruction->setDestinationPointerRegisters );
		LogOperandSet( objControls.pDebugControl, L"Passed or Returned Operands", L"BASIC_BLOCK_INSTRUCTION_PASSED_OR_RETURNED_OPERANDS", pInstruction->setPassedOrReturnedRegisters );
		LogOperandSet( objControls.pDebugControl, L"Compound Operands", L"BASIC_BLOCK_INSTRUCTION_COMPOUND_REGISTERS", pInstruction->setCompoundRegisters );

		itInstruction++;
	}
}


//////////////////////////////////////////////////////////////////////////////////////////////////////
// Analysis Functions
//////////////////////////////////////////////////////////////////////////////////////////////////////

/// Determines that the event in question was NOT an exception
bool
IsEventNotAnException( const DEBUGGER_STATE &objState )
{
	return( objState.dwEventType != DEBUG_EVENT_EXCEPTION );
}

/// Determines if the faulting address is the current instruction pointer
bool IsFaultingAddressInstructionPointer( const DEBUGGER_STATE &objState )
{
	return( objState.offInstruction == objState.offFaultingAddress );
}


// Determines if the faulting address can be disassembled
bool CanFaultingInstructionNotBeDisassebled( const DEBUGGER_STATE &objState )
{
	return (objState.objSourceCode.front()->eClass == NONDISASSEMBLE_INSTRUCTION);
}

/// Determines if the faulting instruction is in Userland
bool IsFaultingInstructionInUserland( const DEBUGGER_STATE &objState )
{
	switch( objState.dwProcessor )
	{
		case IMAGE_FILE_MACHINE_I386:
		case IMAGE_FILE_MACHINE_ARM:
		case IMAGE_FILE_MACHINE_THUMB:
		case IMAGE_FILE_MACHINE_ARMNT:
			return( objState.offInstruction < 0x80000000 );

		case IMAGE_FILE_MACHINE_AMD64:
			return( objState.offInstruction < 0x8000000000000000 );
			break;

		default:
			return( true );
	}
}

// Determines if the faulting instruction is on the stack
bool IsFaultingInstructionOnStack( const DEBUGGER_STATE &objState )
{
	if( (objState.offInstruction != 0UL) &&
		(objState.offInstruction >= objState.offThreadStackLimit) &&
		(objState.offInstruction <= objState.offThreadStackBase ) )
	{
		return( true );
	}
	else
	{
		return( false );
	}
}

/// Determines if the faulting instruction is considered a code flow instruction
bool IsFaultingInstructionControlFlow( const DEBUGGER_STATE &objState )
{
	INSTRUCTION_CLASS eFaultingClass = objState.objSourceCode.front()->eClass;

	if( (eFaultingClass == BRANCH) || (eFaultingClass == RETURN) )
	{
		return( true );
	}
	else
	{
		return( false );
	}
}

/// Determines if the faulting instruction is a block data move
bool IsFaultingInstructionBlockDataMove( const DEBUGGER_STATE &objState )
{
	INSTRUCTION_CLASS eFaultingClass = objState.objSourceCode.front()->eClass;

	return( eFaultingClass == BLOCK_DATA_MOVE );
}

/// Determines if branch selection is based off of the tainted data
///
/// Important: This makes an assumption that branch selection is based off of
///            implicit source registers, and that explicit source registers determine
///            the branch target. 
bool
IsTaintedDataUsedToDetermineBranchSelection( const DEBUGGER_STATE &objState )
{
	INSTRUCTION *pInstruction = objState.objSourceCode.back();
	
	if( pInstruction->eClass == BRANCH )
	{
		for( OPERAND_SET::const_iterator itOperand = pInstruction->setSourceRegisters.begin(); itOperand != pInstruction->setSourceRegisters.end(); itOperand++ )
		{
			if( (pInstruction->setTaintedInputRegisters.find( *itOperand ) != pInstruction->setTaintedInputRegisters.end()) &&
				(pInstruction->setExplicitRegisters.find( *itOperand ) == pInstruction->setExplicitRegisters.end() ) )
			{
				return( true );
			}
		}
	}

	return( false );
}

/// Determines if the branch target is based off of the tainted data
///
/// Important: This makes an assumption that branch selection is based off of
///            implicit source registers, and that explicit source registers determine
///            the branch target. 
bool
IsTaintedDataUsedToDetermineBranchTarget( const DEBUGGER_STATE &objState )
{
	INSTRUCTION *pInstruction = objState.objSourceCode.back();
	
	if( pInstruction->eClass == BRANCH )
	{
		for( OPERAND_SET::const_iterator itOperand = pInstruction->setSourceRegisters.begin(); itOperand != pInstruction->setSourceRegisters.end(); itOperand++ )
		{
			if( (pInstruction->setTaintedInputRegisters.find( *itOperand ) != pInstruction->setTaintedInputRegisters.end()) &&
				(pInstruction->setExplicitRegisters.find( *itOperand ) != pInstruction->setExplicitRegisters.end() ) )
			{
				return( true );
			}
		}
	}

	return( false );
}

/// Determines if tainted data is passed as a function argument
bool
IsTaintedDataUsedAsAFunctionArgument( const DEBUGGER_STATE &objState )
{
	INSTRUCTION *pInstruction = objState.objSourceCode.back();
	
	if( pInstruction->eClass == BRANCH )
	{
		for( OPERAND_SET::const_iterator itOperand = pInstruction->setPassedOrReturnedRegisters.begin(); itOperand != pInstruction->setPassedOrReturnedRegisters.end(); itOperand++ )
		{
			if( pInstruction->setTaintedInputRegisters.find( *itOperand ) != pInstruction->setTaintedInputRegisters.end() )
			{
				return( true );
			}
		}
	}

	return( false );
}

/// Determines if tainted data is returned from a function
bool
IsTaintedDataUsedAsAFunctionRetVal( const DEBUGGER_STATE &objState )
{
	INSTRUCTION *pInstruction = objState.objSourceCode.back();
	
	if( pInstruction->eClass == RETURN )
	{
		for( OPERAND_SET::const_iterator itOperand = pInstruction->setPassedOrReturnedRegisters.begin(); itOperand != pInstruction->setPassedOrReturnedRegisters.end(); itOperand++ )
		{
			if( pInstruction->setTaintedInputRegisters.find( *itOperand ) != pInstruction->setTaintedInputRegisters.end() )
			{
				return( true );
			}
		}
	}

	return( false );
}

/// Determines if the tainted data is used as the destination pointer in a later write
bool
IsTaintedDataUsedInALaterWrite( const DEBUGGER_STATE &objState )
{
	INSTRUCTION_LIST::const_iterator itInstruction = objState.objSourceCode.begin();

	while( itInstruction != objState.objSourceCode.end() )
	{
		INSTRUCTION *pInstruction = *itInstruction;

		if( (pInstruction->eClass == DATA_MOVE) || (pInstruction->eClass == STACK_POP) || (pInstruction->eClass == BLOCK_DATA_MOVE) )
		{
			for( OPERAND_SET::const_iterator itOperand = pInstruction->setDestinationPointerRegisters.begin(); itOperand != pInstruction->setDestinationPointerRegisters.end(); itOperand++ )
			{
				if( pInstruction->setTaintedInputRegisters.find( *itOperand ) != pInstruction->setTaintedInputRegisters.end() )
				{
					return( true );
				}
			}
		}

		itInstruction++;
	}

	return( false );
}

/// Determines if the tainted data is used as source data for a block move
bool
IsTaintedDataUsedAsSourceForBlockDataMove( const DEBUGGER_STATE &objState )
{
	INSTRUCTION_LIST::const_iterator itInstruction = objState.objSourceCode.begin();

	while( itInstruction != objState.objSourceCode.end() )
	{
		INSTRUCTION *pInstruction = *itInstruction;

		if( pInstruction->eClass == BLOCK_DATA_MOVE )
		{
			for( OPERAND_SET::const_iterator itOperand = pInstruction->setSourceRegisters.begin(); itOperand != pInstruction->setSourceRegisters.end(); itOperand++ )
			{
				if( pInstruction->setTaintedInputRegisters.find( *itOperand ) != pInstruction->setTaintedInputRegisters.end() )
				{
					return( true );
				}
			}
		}

		itInstruction++;
	}

	return( false );
}

/// Determines if the trace contains unknown function symbols
bool
DoesStackTraceContainUnknownFunctions( const DEBUGGER_STATE &objState )
{
	return( objState.fStackContainsUnknownSymbols );
}

/// Determines if there was a bug check found during analysis
bool
WasBugCheckDetected( const DEBUGGER_STATE &objState )
{
	return( objState.fBugCheckDetected );
}

/// Determines if the exception handler chain was found to be corrupted
bool
WasExceptionHandlerChainCorrupted( const DEBUGGER_STATE &objState )
{
	return( objState.fExceptionChainCorruptionDetected );
}

/// Determines if there was an application verifier stop found
bool
WasApplicationVerifierStopDetected( const DEBUGGER_STATE &objState )
{
	return( objState.fApplicationVerifierStopDetected );
}



//////////////////////////////////////////////////////////////////////////////////////////////////////
// Logging Functions
//////////////////////////////////////////////////////////////////////////////////////////////////////

///
/// Set the logging mode for the extension
///
void
SetLoggingMode( _LOGGING_MODE eMode )
{
	s_eLoggingMode = eMode;
}


///
/// Set the hashing Function
///
void
SetHashingMode( _HASHING_MODE eMode )
{
	s_eHashingMode = eMode;
}

///
/// Log the value, based on the current logging rules. Note that the logging buffer is limited to 
/// 4096 characters.
///
void
Log( __in PDEBUG_CONTROL4 pDebugger, LOGGING_LEVEL eLevel, LOGGING_TYPE eType, __in const WCHAR *pwzFormat, ... )
{
	va_list va;
	va_start( va, pwzFormat );

	Log( pDebugger, eLevel, eType, pwzFormat, va );

	va_end( va );
}

///
/// Log the value, based on the current logging rules. Note that the logging buffer is limited to 
/// 4096 characters.
///
void
Log( __in PDEBUG_CONTROL4 pDebugger, LOGGING_TYPE eType, __in const WCHAR *pwzFormat, ... )
{
	va_list va;
	va_start( va, pwzFormat );

	Log( pDebugger, TERSE, eType, pwzFormat, va );

	va_end( va );
}

///
/// Log the value, based on the current logging rules. Note that the logging buffer is limited to 
/// 4096 characters.
///
void
Log( __in PDEBUG_CONTROL4 pDebugger, LOGGING_LEVEL eLevel, LOGGING_TYPE eType, __in const WCHAR *pwzFormat, va_list va )
{

	if( s_eLoggingMode == NONE )
	{
		return;
	}

	if( (s_eLoggingMode & eType) != eType )
	{
		return;
	}

	WCHAR pwzBuffer[4096];
	int cchBuffer  = _vsnwprintf_s( pwzBuffer, _TRUNCATE, pwzFormat, va );

    UNREFERENCED_PARAMETER(cchBuffer);

	if( eLevel == TERSE )
	{
		pDebugger->OutputWide( DEBUG_OUTPUT_NORMAL, L"%s", pwzBuffer );
	}
	else if( s_eLoggingMode & VERBOSE )
	{
		pDebugger->OutputWide( DEBUG_OUTPUT_NORMAL, L"%s", pwzBuffer );
	}
}

///
// Log an OperandSet contents (with a specified prefix for both the human readable and machine readable forms
void
LogOperandSet( __in PDEBUG_CONTROL4 pDebugger, __in const WCHAR *pwzHumanPrefix, __in const WCHAR *pwzMachinePrefix, __in const OPERAND_SET& setOperands  )
{
	bool fOperandFound = false;

	for( OPERAND_SET::const_iterator itOperand = setOperands.begin(); itOperand!= setOperands.end(); itOperand++ )
	{
		if( !fOperandFound )
		{
			Log( pDebugger, VERBOSE, HUMAN, L"       %s: '%.*s'", pwzHumanPrefix, itOperand->cchOperand, itOperand->pwzOperand );
			Log( pDebugger, MACHINE, L"%s:%.*s\n", pwzMachinePrefix, itOperand->cchOperand, itOperand->pwzOperand );
			fOperandFound = true;
		}
		else
		{
			Log( pDebugger, VERBOSE, HUMAN, L",'%.*s'", itOperand->cchOperand, itOperand->pwzOperand );
			Log( pDebugger, MACHINE, L"%s:%.*s\n", pwzMachinePrefix, itOperand->cchOperand, itOperand->pwzOperand );
		}
	}

	if( fOperandFound )
	{
		Log( pDebugger, VERBOSE, HUMAN, L"\n" );
	}
}
